/*
 * @Author: your name
 * @Date: 2021-08-09 18:33:26
 * @Description: In User Settings Edit
 * @FilePath: /wx-weapp/utils/enhancer/core/observe.js
 */

import { isObject, hasProto, isPlainObject, hasOwn } from "../utils/util";
import { def } from "../utils/observer";
import { arrayMethods, dependArray } from "./array";
import Dep from './dep'

const arrayKeys = Object.getOwnPropertyNames(arrayMethods);
export class Observer {
  constructor(data, vm, parrentKey = "",parentDep) {
    if (!isObject(data)) {
      return;
    }
    this.dep = new Dep();
    this.parrentKey = parrentKey;
    this.parentDep = parentDep
    this.vm = vm;
    def(data, "__ob__", this); //被观察的对象添加"__ob__"属性
    if (Array.isArray(data)) {
      // 替换原型
      if (hasProto) {
        this.protoAugment(data, arrayMethods);
      } else {
        this.copyAugment(data, arrayMethods, arrayKeys);
      }
    }
    this.observe(data, parrentKey,parentDep)
  }
  initObserver(value, vm, parrentKey,parentDep){
    if (!isObject(value)) return;
    let ob = null;
    if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
        ob = value.__ob__;
    } else if (isPlainObject(value) || Array.isArray(value)) {
        ob = new Observer(value, vm, parrentKey,parentDep);
    }
    return ob;
  }
  observe(data, parrentKey,parentDep) {
    Object.keys(data).forEach((key) => {
      this.defineReactive(data, key, data[key], parrentKey,parentDep);
    });
  }
  defineReactive(obj, key, value, parrentKey,parentDep) {
    const relkey = `${parrentKey ? parrentKey + "." : ""}${key}`;
    //获取指定对象的自身属性描述符。自身属性描述符是指直接在对象上定义（而非从对象的原型继承）的描述符
    const property = Object.getOwnPropertyDescriptor(obj, key);
    //属性描述不可被改变或者属性不可被删除时直接返回
    if (property && property.configurable === false) {
      return;
    }

    //收集依赖
    const dep = new Dep()
    // new Observer(value, this.vm, relkey);
    let childOb = this.initObserver(value, this.vm, relkey,dep)
    Object.defineProperty(obj, key, {
      enumerable: true,
      configurable: true,
      //小程序获取不到模板内容，故不在get中收集依赖
      get() {
        if(Dep.target){
          dep.depend() //依赖收集
          if (childOb) {
            childOb.dep.depend();
            if (Array.isArray(value)) {
                dependArray(value);
            }
          }
        }
        return value;
      },
      set: (nval) => {
        if (value === nval) return;
        //防止设置的值是一个对象
        childOb = this.initObserver(nval, this.vm, relkey,dep)
        value = nval;
        dep.notify()  //通知watcher数据更新
        parentDep && parentDep.notify() //如果当前设置的值是对象的属性，则通知属性的上一层更新（解决deep watch）
        this.vm.__isObserverData && this.updateView(relkey, nval);
      },
    });
  }
  protoAugment(target, src) {
    target.__proto__ = src;
  }
  copyAugment(target, src, keys) {
    for (let i = 0, l = keys.length; i < l; i++) {
      const key = keys[i];
      def(target, key, src[key]);
    }
  }
  updateView(relkey, value) {
    relkey = relkey
      .split(".")
      .map((i) => {
        if (!isNaN(Number(i))) {
          i = `[${i}]`;
        }
        return i;
      })
      .join(".");
    this.vm.setData({
      [relkey]: value,
    });
  }
}
